package filechat;
// <editor-fold defaultstate="collapsed" desc="imports">
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.event.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.util.ArrayList;
import java.util.concurrent.*;
import javax.swing.*;
// </editor-fold>

public class Client implements ObjectStreamListener {
    
    //<editor-fold defaultstate="collapsed" desc="Variables Declaration">
    // Threads...
    static ThreadPoolExecutor executor;
    CheckConnectionsThread connectionUpdateThread = new CheckConnectionsThread();
    // MainFrame(GUI) variables
    MainFrame clientMainFrame = new MainFrame();
    AddContactWindow addContactWindow;
    ClientActionListener actionListener = new ClientActionListener();
    ListTransferHandler transferHandler = new ListTransferHandler();
    URI websiteURI;
    // Client-object variables
    String username;
    String IP;
    String email;
    // Server connection/communication variables
    static final String ServerIP = "130.238.15.122";
    static final int serverConnectionPort = 4006;   // Temporarily the standard port for connections to the server
    String serverData;
    Object[] listDataObjects;
    int objectType;
    boolean authentificationBoolean;
    int searchResult;
    Socket serverConnectionSocket;
    ObjectOutputStream serverOutputStream;
    ObjectInputStream serverInputStream;
    ObjectStreamManager serverStreamManager;
    CountDownLatch authenticateLatch;
    CountDownLatch searchForUserLatch;
    // P2P connection variables
    ServerSocket receiveConnectionsSocket;
    static final int clientConnectionsPort = 4001;
    //    int groupConnectionPort = 4003; HOW TO ENABLE SEVERAL GROUP CONNECTIONS? / ENABLE SEVERAL CONNECTIONS BETWEEN PEERS?!
    int incomingConnectionNumber = 1;
    int outgoingConnectionNumber = 1;
    ArrayList<Connection> incomingConnectionList = new ArrayList();     // ArrayList containing inbound Connection-objects
    ArrayList<Connection> outgoingConnectionList = new ArrayList();     // ArrayList containing outgoing Connection-objects
    ArrayList<Socket> groupChatSockets = new ArrayList();       // ArrayList containing sockets related to a group chat
    ArrayList<Contact> contactList = new ArrayList();       // ArrayList containing Contact-objects
    //</editor-fold>

    // Client constructor
    Client() throws InterruptedException {
        // Get IP-address of localhost and set the websiteURI
        try {
            IP = InetAddress.getLocalHost().getHostAddress();
            websiteURI = new URI("http://www.ftcp.stsvt12.student.it.uu.se/Register%20user.html");
        } catch (Exception e) {
            System.out.println("IP or websiteURI error: "+e.getMessage());
        }
        // Starts a connection with the server
        startServerConnection();
        System.out.println("Server connection started");
        startThreadPoolExecutor();

        boolean login = clientLogin();
        while (!login) { // While incorrect email or password, open dialog-window and initiate new login attempt
            JOptionPane.showMessageDialog(null, "Wrong Email or Password!", "Login Error", JOptionPane.INFORMATION_MESSAGE);
            login = clientLogin();
        }
        // Start looking for incoming connections
        receiveConnections();     
    }
    
    // <editor-fold defaultstate="collapsed" desc="startServerConnection">
    /*
     * Creates socket-connection to server and starts IO-streams
     */
    void startServerConnection() {
        try {
            serverConnectionSocket = new Socket(ServerIP, serverConnectionPort);
            serverOutputStream = new ObjectOutputStream(serverConnectionSocket.getOutputStream());
            serverInputStream = new ObjectInputStream(serverConnectionSocket.getInputStream());
            serverStreamManager = new ObjectStreamManager(0, serverInputStream, this);
        } catch (Exception e) {
            displayErrorMessage("Could not connect to the server: ", e);
            System.exit(0);
        } 
    }    // </editor-fold>

    //<editor-fold defaultstate="collapsed" desc="clientLogin">
    /*
     * Creates & opens a dialog login-window where the user enters email and password 
     * The data is then sent to the server and the client waits for a reply (true or false) 
     * If true: start mainFrame...
     * If false: Return false
     */
    boolean clientLogin() {
        // Create a button that on click opens the website in the systems standard browser
        JButton forgotPasswordButton = new JButton();
        forgotPasswordButton.setText("Forgot your password?");
        forgotPasswordButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    Desktop desktop = java.awt.Desktop.getDesktop();
                    desktop.browse(websiteURI);
                } catch (IOException ioe) {
                    System.out.println("forgotPasswordButton I/Oexc: "+ioe.getMessage());
                }
            }
        });
        
        // Create and add the other components to the login dialog window
        JTextField emailInputField = new JTextField();
        JPasswordField passwordInputField = new JPasswordField();
        Object[] loginInputData = {"Email:", emailInputField, "Password:", passwordInputField, forgotPasswordButton};
        JOptionPane loginOptionPane = new JOptionPane(loginInputData, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null, null);
        JDialog loginDialog = loginOptionPane.createDialog("Login");
        loginDialog.addWindowListener(new ClientWindowListener());
        loginDialog.setVisible(true);
        int result = JOptionPane.OK_OPTION;
        try {
            result = ((Integer) loginOptionPane.getValue()).intValue();
        } catch (Exception e) {
            System.out.println("Login error: "+e.getMessage());
        }
        
        if (result == JOptionPane.OK_OPTION) {
            sendLoginInfo(emailInputField.getText(), new String(passwordInputField.getPassword()));
            System.out.println("login info sent");
            // boolean userAccess = loginAuthentication();
            if (loginAuthentication()) {
                System.out.println("authenticated!");
                email = emailInputField.getText();
                clientMainFrame.emailField.setText(email);
                startMainFrame();
            } else { // Return false if the login info is wrong...
                return false;
            }
        } else { // Exit on cancel
            closeServerConnection();
            System.exit(0);
        }
        return true;
    } //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="startMainFrame">
    // Adds listeners and starts the mainFrame
    void startMainFrame() {
        // Creates mainFrame, adds mouseListener to contactDisplayList ?!METHOD!?
        clientMainFrame.addWindowListener(new ClientWindowListener());
        clientMainFrame.onlineContactList.addMouseListener(new ClientMouseListener());
//        clientMainFrame.offlineContactList.addMouseListener(); värt?
        clientMainFrame.editButton.addActionListener(actionListener);
        clientMainFrame.editInfoMenuItem.addActionListener(actionListener);
        clientMainFrame.addContactButton.addActionListener(actionListener);
        clientMainFrame.addContactMenuItem.addActionListener(actionListener);
        clientMainFrame.exitMenuItem.addActionListener(actionListener);
        clientMainFrame.removeContactPopupMenuItem.addActionListener(actionListener);
        clientMainFrame.startChatPopupMenuItem.addActionListener(actionListener);
        clientMainFrame.groupChatButton.addActionListener(actionListener);
        clientMainFrame.onlineContactList.setTransferHandler(transferHandler);
        clientMainFrame.groupChatList.setTransferHandler(transferHandler);
        clientMainFrame.groupChatList.addKeyListener(new ClientKeyListener());
        //addMainFrameIcon();
        clientMainFrame.IPField.setText(IP);
        clientMainFrame.setVisible(true);
    } //</editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="sendLoginInfo">
    /*
     * Sends the clients login-info (email+password) to the server The server
     * checks the database and returns ...
     */
    void sendLoginInfo(String email, String pass) {
        try {
            serverOutputStream.writeObject(1 + "\n" + email + "\n" + pass);     // 1(+\n) is inserted before login info to identify that it is a login request that is sent
            serverOutputStream.flush();
        } catch (IOException e) {
            displayErrorMessage("Could not send login info: ", e);
        }
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="loginAuthentication">
    /*
     * Waits for the server to send a boolean-object on the stream Returns the
     * boolean value (true or false), or false if something goes wrong
     */
    boolean loginAuthentication() {
        authenticateLatch = new CountDownLatch(1);
        try {
            authenticateLatch.await();
        } catch (InterruptedException ex) {
            System.out.println("loginAuthentication interrupted: "+ex.getMessage());
        }
        if (authentificationBoolean) {
            return true;
        } else if (!authentificationBoolean) {
            return false;
        }
        return false;
    }// </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="receiveConnections">
    /*
     * Creates the serversocket and listens for connections On connection
     * creating new Connection object
     */
    public void receiveConnections() {
        System.out.println("receiving connections...");
        try {
            receiveConnectionsSocket = new ServerSocket(clientConnectionsPort);
        } catch (Exception e) {
            System.out.println("receiveConnenctionSocket Error: "+ e.getMessage());
        }
        try {
            while (receiveConnectionsSocket != null) {
                // Checks if the CheckConnectionsThread is running
                if (!connectionUpdateThread.isAlive()) {
                    connectionUpdateThread.start();
                }
                // Wait for incoming connection
                Socket inSocket = receiveConnectionsSocket.accept();
                System.out.println("connection received!");
                
                String contactName = null;
                String socketIP = inSocket.getInetAddress().getHostAddress();
                for (int i = 0; i < contactList.size(); i++) {
                    if(contactList.get(i).contactIP == null ? socketIP == null : contactList.get(i).contactIP.equals(socketIP)) {
                        contactName = contactList.get(i).contactUsername;
                    }
                }
                
                Connection inConnection = new Connection(inSocket, incomingConnectionNumber, contactName, username, true);
                incomingConnectionList.add(inConnection);
                incomingConnectionNumber++;
            }
        } catch (Exception e) {
            System.out.println("receiveConnection Error: "+ e.getMessage());
        }
    } // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="startP2PConnection">
    /*
     * Attempts to start a new connection with a contact --> Creates a new
     * Connection with this contact and adds it to the outGoingConnectionList
     * Args: (String) IP-address
     */
    public void startP2PConnection(String contactName, String ip) {
        // Checks if the CheckConnectionsThread is running
        if (!connectionUpdateThread.isAlive()) {
            connectionUpdateThread.start();
        }
        System.out.println("startP2PConnection..");
        System.out.println("attempting to connect to: " + ip);
        try {
            Connection outConnection = new Connection(new Socket(ip, clientConnectionsPort), outgoingConnectionNumber, contactName, username, false);
            outgoingConnectionList.add(outConnection);
            outgoingConnectionNumber++;
            System.out.println("P2Pconnection started!");
        } catch (Exception e) {
            displayErrorMessage("Could not start connection: ", e);
        }
    } // </editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="startGroupChat">
    /*
     * Attempts to create and add socket-connections to ArrayList groupChatSockets
     * Uses the list of sockets to create a GroupConnection object
     * Args: Object[] contacts
     */
    void startGroupChat(Object[] contacts) {
        String contactNames = "";
        groupChatSockets.clear();
        for (int i = 0; i < contactList.size(); i++) {
            for (int j = 0; j < contacts.length; j++) {
                if (contactList.get(i).contactUsername.equals(contacts[j])) {
                    contactNames = contactNames + contactList.get(i).contactUsername + ", ";
                    try {
                        groupChatSockets.add(new Socket(contactList.get(i).contactIP, clientConnectionsPort));
                    } catch (Exception ex) {
                        displayErrorMessage("Could not start group chat: ", ex);
                    }
                }
            }
        }
        GroupConnection groupConnection = new GroupConnection(groupChatSockets, contactNames, username);
    }
    //</editor-fold>
        
    // <editor-fold defaultstate="collapsed" desc="activeConnection">
    /*
     * Checks if a connection is active.
     * Args: (String) IP
     * Returns: True if a connection to the given IP is active, otherwise False
     */
    boolean activeConnection(String IP) {
        if (!incomingConnectionList.isEmpty()) {
            for (int i = 0; i < incomingConnectionList.size(); i++) {
                if(incomingConnectionList.get(i).connectionIP.equals(IP)) {
                    System.out.println("connection active to: "+incomingConnectionList.get(i).contactUsername);
                    incomingConnectionList.get(i).chatWindow.setVisible(true); // Works? focus to chatwindow...
                    return true;
                }
            }
        }
        if (!outgoingConnectionList.isEmpty()) {
            for (int j = 0; j < outgoingConnectionList.size(); j++) {
                if (outgoingConnectionList.get(j).connectionIP.equals(IP)) {
                    System.out.println("connection active to: "+outgoingConnectionList.get(j).contactUsername);
                    outgoingConnectionList.get(j).chatWindow.setVisible(true); // Works?
                    return true;
                }
            }
        }
        return false;
    } // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="setListObjects">
    /*
     * Creates an Object-array and fills it with the usernames of the
     * Contact-objects in the contactList These objects are then added to the
     * graphical contact list
     */
    public void setListObjects() {
        // Makes sure the contact list is displayed correctly
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                clientMainFrame.onlineListModel.clear();
                clientMainFrame.offlineListModel.clear();
                for (int i = 0; i < contactList.size(); i++) {
                    if (contactList.get(i).online) {
                        clientMainFrame.onlineListModel.addElement(contactList.get(i).contactUsername);
                    } else {
                        clientMainFrame.offlineListModel.addElement(contactList.get(i).contactUsername);
                    }
                }
            }
        });
    }
    // </editor-fold>

    //<editor-fold defaultstate="collapsed" desc="objectReceived">
    // Handles objects received from the server
    @Override
    public void objectReceived(int number, Object object, Exception exception) {        
        if (exception == null) {
            objectType = checkObject(object);
            // When attempting to login
            if (objectType == 1) {
                System.out.println("boolean received");
                serverData = (String) object;
                authentificationBoolean = Boolean.parseBoolean(serverData.split("\n")[1]);
                authenticateLatch.countDown();
            } // When receiving username from server
            else if (objectType == 2) {
                serverData = (String) object;
                username = serverData.split("\n")[1];
                // Set the mainFrame title
                clientMainFrame.setTitle(username);
                clientMainFrame.usernameField.setText(username);
                System.out.println("username received: "+ username);
            } // When the server receives a contact search result
            else if (objectType == 3) {
                serverData = (String) object;
                searchResult = Integer.parseInt(serverData.split("\n")[1]);
                System.out.println("Friend request response received: "+searchResult);
                searchForUserLatch.countDown();
            } // When the contact list is updated
            else if (objectType == 4){
                System.out.println("received contact list");
                contactList = (ArrayList<Contact>) object;
                if (!contactList.isEmpty()) {
                    setListObjects();
                }
            }
        } else {
            System.err.println("objectReceived-Error: " + exception);
        }
    } //</editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="checkObject">
    /*
     * Takes an object, converts it to a string, splits it at \n and returns the first value (int)
     * Args: Object. Returns: int 
     */
    public int checkObject(Object object) {
        try {
            if (object.getClass().equals(ArrayList.class)){
                return 4;
            }
            else {
                serverData = (String) object;
                return Integer.parseInt(serverData.split("\n")[0]);
            }
        } catch(Exception e) {
            System.out.println("checkObject exception: "+e.getMessage());
            serverData = (String) object;
            return Integer.parseInt(serverData.split("\n")[0]);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="searchForUser">
    /*
     * Tries to contact the server and search for a user by an email entered in
     * the addContactWindow Depending on the result from the server display a
     * message dialog
     */
    void searchForUser() {
        if (!addContactWindow.enterEmailTextField.getText().isEmpty()) {
            try {
                serverOutputStream.writeObject(2 + "\n" + email + "\n" + addContactWindow.enterEmailTextField.getText());
            } catch (IOException ex) {
                displayErrorMessage("Search Error! :", ex);
                return;
            }
            // Searching ...
            addContactWindow.descriptionLabel.setText("Searching...");
            addContactWindow.searchButton.setEnabled(false);
            addContactWindow.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); // Enable wait cursor            
            // Wait for result..
            try {
                System.out.println("awaiting latch");
                searchForUserLatch.await();
            } catch (InterruptedException ex) {
                displayErrorMessage("Search interrupted! ", ex);
                return;
            }
            addContactWindow.searchButton.setEnabled(true);
            addContactWindow.setCursor(null); // Turn off the wait cursor
            addContactWindow.descriptionLabel.setText("Done!");
            // Search finished
            if (searchResult == 1) {
                JOptionPane.showMessageDialog(null,
                        "User found and added to contact list",
                        "Search finished!",
                        JOptionPane.INFORMATION_MESSAGE);
            } else if (searchResult == 2) {
                JOptionPane.showMessageDialog(null,
                        "No user found or already in contact list",
                        "Search finished!",
                        JOptionPane.INFORMATION_MESSAGE);
            }
            addContactWindow.dispose();

        } else {
            JOptionPane.showMessageDialog(null,
                    "Enter valid email address!",
                    "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
    }// </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="removeContact">
    /*
     * Sends a request to the server to remove a given Contact from the users contactList
     * Args: (String) username
     */
    void removeContact(String username) {
        for (int i = 0; i < contactList.size(); i++) {
           if (contactList.get(i).contactUsername.equals(username)) {
                try {
                    serverOutputStream.writeObject(3 + "\n" + email + "\n" + contactList.get(i).contactEmail);
                } catch (IOException ex) {
                    displayErrorMessage("Could not remove contact: ", ex);
                }
           }
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="startThreadPoolExecutor">
    /*
     * This method starts a ThreadPoolExecutor that is used in the ObjectStreamManager class. The ThreadPoolExecutor 
     * handles a number of worker threads (defined in ObjectStreamManager) that in turn handle incoming objects on the streams.
     */
    public static void startThreadPoolExecutor() {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(100, true);
        executor = new ThreadPoolExecutor(
                5, // core amount of threads
                10, // max amount of threads
                1, // keep alive time
                TimeUnit.MINUTES, // keep alive time unit
                queue);
    }//</editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="displayErrorMessage">
    /*
     * Opens a error dialog window and displays the error message Args: error
     * text (string), exception
     */
    public void displayErrorMessage(String errorString, Exception e) {
        JOptionPane.showMessageDialog(null,
                errorString + e.getMessage(),
                "Error",
                JOptionPane.ERROR_MESSAGE);
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="closeServerConnection">
    /*
     * Close connection to the server
     */
    public void closeServerConnection() {
        if (serverConnectionSocket != null || !serverConnectionSocket.isClosed()) {
            try {
                serverConnectionSocket.close();
                serverStreamManager.closeManager();
                serverInputStream.close();
                serverOutputStream.close();
            } catch (Exception e) {
                System.out.println("Error in closing server connection: "+ e.getMessage());
            }
        }
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="closeClient">
    /*
     * Close all (active) connections
     */
    public void closeClient() {
        try {
            if (serverConnectionSocket != null || !serverConnectionSocket.isClosed()) {
                serverConnectionSocket.shutdownOutput();
                serverConnectionSocket.close();
            }
            if (serverStreamManager != null) {
                serverStreamManager.closeManager();
            }
            if (serverInputStream != null) {
                serverInputStream.close();
            }
            if (serverOutputStream != null) {
                serverOutputStream.close();
            }
            if (receiveConnectionsSocket != null) {
                receiveConnectionsSocket.close();
            }
            for (int i = 0; i < incomingConnectionList.size(); i++) {
                if (incomingConnectionList.get(i).connectionSocket != null || !incomingConnectionList.get(i).connectionSocket.isClosed()) {
                    incomingConnectionList.get(i).connectionSocket.close();
                }
                if (incomingConnectionList.get(i).connectionStreamManager != null) {
                    incomingConnectionList.get(i).connectionStreamManager.closeManager();
                }
                if (incomingConnectionList.get(i).connectionInputStream != null) {
                    incomingConnectionList.get(i).connectionInputStream.close();
                }
                if (incomingConnectionList.get(i).connectionOutputStream != null) {
                    incomingConnectionList.get(i).connectionOutputStream.close();
                }
            }
            for (int j = 0; j < outgoingConnectionList.size(); j++) {
                if (outgoingConnectionList.get(j).connectionSocket != null || !outgoingConnectionList.get(j).connectionSocket.isClosed()) {
                    outgoingConnectionList.get(j).connectionSocket.close();
                }
                if (outgoingConnectionList.get(j).connectionStreamManager != null) {
                    outgoingConnectionList.get(j).connectionStreamManager.closeManager();
                }
                if (outgoingConnectionList.get(j).connectionInputStream != null) {
                    outgoingConnectionList.get(j).connectionInputStream.close();
                }
                if (outgoingConnectionList.get(j).connectionOutputStream != null) {
                    outgoingConnectionList.get(j).connectionOutputStream.close();
                }
            }
        } catch (IOException e) {
            System.out.println("close-error..");
        }
    }// </editor-fold>
        
    // <editor-fold defaultstate="collapsed" desc="INNER/LISTENER CLASSES">
//********************* INNER CLASSES / LISTENERS ****************************\\    
    // Listens for action events
    private class ClientActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            // Opens a new addContactWindow
            if (e.getSource().equals(clientMainFrame.addContactButton) || e.getSource().equals(clientMainFrame.addContactMenuItem)) {
                addContactWindow = new AddContactWindow();
                addContactWindow.searchButton.addActionListener(this);
                searchForUserLatch = new CountDownLatch(1);
                addContactWindow.setVisible(true);
            } 
            // Close streams etc. and exit
            else if (e.getSource().equals(clientMainFrame.exitMenuItem)) {
                closeClient();
                System.exit(0);
            } 
            // Calls the removeContact() method with the selected value as argument
            else if (e.getSource().equals(clientMainFrame.removeContactPopupMenuItem)) {
                removeContact((String) clientMainFrame.onlineContactList.getSelectedValue());                
            } 
            // Calls the startP2PConnection() method given selected value (if..)
            else if (e.getSource().equals(clientMainFrame.startChatPopupMenuItem)) {
                for (int i = 0; i < contactList.size(); i++) {
                    if (contactList.get(i).contactUsername.equals(clientMainFrame.onlineContactList.getSelectedValue())                             
                            && !activeConnection(contactList.get(i).contactIP)) {                        
                        startP2PConnection(contactList.get(i).contactUsername, contactList.get(i).contactIP);
                    }
                }
            } 
            // Calls the startGroupChat() method with the contacts currently in the list
            else if (e.getSource().equals(clientMainFrame.groupChatButton)) {
                if (!clientMainFrame.groupChatListModel.isEmpty()) {
                    if(clientMainFrame.groupChatListModel.getSize() > 1) {
                        startGroupChat(clientMainFrame.groupChatListModel.toArray());
                        clientMainFrame.groupChatListModel.clear();
                    } else {
//                        startP2PConnection();
                        System.out.println("only one contact in list...");
                    }
                }                
            } 
            // Opens the website in the sytems default browser
            else if (e.getSource().equals(clientMainFrame.editButton) || e.getSource().equals(clientMainFrame.editInfoMenuItem)) {
                try {
                    Desktop desktop = java.awt.Desktop.getDesktop();
                    desktop.browse(websiteURI);
                } catch (IOException ioe) {
                    System.out.println("edit I/Oexc: "+ioe.getMessage());
                }
            }
            // Calls the searchForUser() method
             else if (e.getSource().equals(addContactWindow.searchButton)) {
                    searchForUser();
            } 
        }
    }
    // Listens for window events
    private class ClientWindowListener extends WindowAdapter {
        @Override
        public void windowClosing(WindowEvent e) {
            closeClient();
            System.exit(0);
        }
    }
    // Listens for mouse events (and creates a popup menu if an item is selected and clicked on)
    private class ClientMouseListener extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger()) {
                doPop(e);
            }
        }
        @Override
        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger() && e.getComponent().isFocusOwner()) {
                doPop(e);
            }
        }
        @Override
        public void mouseClicked(MouseEvent evt) {
            if (evt.getClickCount() == 2) {
                for (int i = 0; i < contactList.size(); i++) {
                    if (contactList.get(i).contactUsername.equals(clientMainFrame.onlineContactList.getSelectedValue())
                            && !activeConnection(contactList.get(i).contactIP)) {                        
                        startP2PConnection(contactList.get(i).contactUsername, contactList.get(i).contactIP);
                    }
                }
            }
        }
        
        private void doPop(MouseEvent e) {
            // make it pop only if the selected value is marked!
            if (!clientMainFrame.onlineContactList.isSelectionEmpty()) {                
                clientMainFrame.listPopupMenu.show(e.getComponent(), e.getX(), e.getY());
            }
        }
    }
    // Listens for key events
    private class ClientKeyListener implements KeyListener {
        @Override
        public void keyTyped(KeyEvent e) {
            if (e.getKeyChar() == KeyEvent.VK_DELETE && !clientMainFrame.groupChatList.isSelectionEmpty()) {
                Object[] selected = clientMainFrame.groupChatList.getSelectedValues();
                for (int i = 0; i < selected.length; i++) {
                    clientMainFrame.groupChatListModel.removeElement(selected[i]);
                }                                        
            }
        }
        @Override
        public void keyPressed(KeyEvent e) {}
        @Override
        public void keyReleased(KeyEvent e) {}        
    }
    // Checks if there are any inactive connections in the lists and removes them if that is the case
    private class CheckConnectionsThread extends Thread {
        @Override
        public void run() {
            System.out.println("Running checkConnectionThread");
            while (receiveConnectionsSocket != null) {
                if (!incomingConnectionList.isEmpty()) {
                    for (int i = 0; i < incomingConnectionList.size(); i++) {
                        try {
                            if (incomingConnectionList.get(i).connectionSocket.isClosed() || incomingConnectionList.get(i).connectionSocket == null) {
                                System.out.println("connection removed: "+incomingConnectionList.get(i).connectionIP);                                
                                incomingConnectionList.remove(i);                                
                                incomingConnectionNumber--;
                            }
                        } catch (NullPointerException e) {
                            System.out.println(e.getMessage());
                        }
                    }
                }
                if (!outgoingConnectionList.isEmpty()) {
                    for (int j = 0; j < outgoingConnectionList.size(); j++) {
                        try {
                            if (outgoingConnectionList.get(j).connectionSocket.isClosed() || outgoingConnectionList.get(j).connectionSocket == null) {
                                System.out.println("connection removed: "+outgoingConnectionList.get(j).connectionIP);
                                outgoingConnectionList.remove(j);
                                outgoingConnectionNumber--;
                            }
                        } catch (NullPointerException e) {
                            System.out.println(e.getMessage());
                        }
                    }
                }
            }
        }
    }
//****************************************************************************\\
// </editor-fold>    
}   // End of Client
